package tom.spring.cloud.gateway;

import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.security.Principal;

/**
 * 基于配置的gateway, GlobalFilter应用于所有请求都生效过滤器, GatewayFilter为特定过滤器配置了才启用
 * , profiles: default
 * http://localhost:8090/actuator/gateway/routes
 * 屏蔽spring-security
 * @author ZHUFEIFEI
 */
@Slf4j
@EnableWebFlux
@EnableDiscoveryClient
@SpringBootApplication
@ComponentScan(
        excludeFilters = {
        @ComponentScan.Filter(type = FilterType.REGEX,
                pattern = "tom\\.spring\\.cloud\\.gateway\\..*"
        )
})
public class GlobalFilterDemo01 {

    public static void main(String[] args) {
        SpringApplication.run(GlobalFilterDemo01.class, args);
    }


    /**
     before global filter a
     before global filter b
     before global filter c
     after global filter c
     after global filter b
     after global filter a
     * @return
     */
    @Bean
    @Order(-1)
    public GlobalFilter a() {
        return ((exchange, chain) -> {
            log.info("before global filter a");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> log.info("after global filter a")));
        });
    }

    @Bean
    @Order(0)
    public GlobalFilter b() {
        return ((exchange, chain) -> {
            log.info("before global filter b");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> log.info("after global filter b")));
        });
    }

    @Bean
    @Order(1)
    public GlobalFilter c() {
        return ((exchange, chain) -> {
            log.info("before global filter c");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> log.info("after global filter c")));
        });
    }


    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("global_filter_demo"
                        , r -> r.path("/hello").uri("http://localhost:8021")
                ).build();
    }

    @Configuration
    @EnableConfigurationProperties(WebFluxProperties.class)
    class MyWebFluxConfigurer implements WebFluxConfigurer {

        @Override
        public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
            //打印header详情
            configurer.defaultCodecs().enableLoggingRequestDetails(true);
        }
    }

    /**
     * pre GlobalFilter
     * @return
     */
    @Bean
    public GlobalFilter customGlobalFilter() {
        return (exchange, chain) -> exchange.getPrincipal()
                .map(Principal::getName)
                .defaultIfEmpty("Default User")
                .map(userName -> {
                    //adds header to proxied request
                    exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
                    return exchange;
                })
                .flatMap(chain::filter);
    }

    /**
     * Post GlobalFilter
     * @return
     */
    @Bean
    public GlobalFilter customGlobalPostFilter() {
        return (exchange, chain) -> chain.filter(exchange)
                .then(Mono.just(exchange))
                .map(serverWebExchange -> {
                    //adds header to response
                    serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
                            HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
                    return serverWebExchange;
                })
                .then();
    }

    @EnableWebFluxSecurity
    class MyFluxSecurityConfig {

        @Bean
        public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
            //允许所有请求
            return http.authorizeExchange().anyExchange().permitAll().and().build();
        }

    }
}
